#include "ustc_comdef.h"
#include "i2c_drv.h"
#include "ustc_hif.h"

#define RSP_I2C_TIMEOUT			0x0200
#define RSP_I2C_NO_ACK			0x0201
#define RSP_I2C_RETRY			0x0202

#if 0
/*===========================================================================
FUNCTION:  i2c_init

DESCRIPTION: 
  	iic interface initilize
  	
INPUT PARAMETER:

OUTPUT PARAMETER:  

RETURN VALUE:

===========================================================================*/
void i2c_init(I2C_PARA_T *p_para_i2c)
{
	stack_check();

	// I2C module reset, equels the CMIF logic reset
	//volans, maybe called at any time(when CMIF work,so don't reset; and caller gurantee CMIF reset before this call
	//ustc_io_reset_module(MODULE_CMIF_LOGIC);

	/* set time parameter*/
	if (p_para_i2c->timming==0)//default
	{
		MA_OUTW(HW_I2C_TIMING,0x7c1120e0);      /*set 100k, it can fit with all sensor.*/ 
	}
	else
		MA_OUTW(HW_I2C_TIMING,p_para_i2c->timming);
 
	/* close i2c interrupt */
	MA_OUTW(HW_I2C_INT_MASK,7);	
 	/* clear state */
	MA_OUTW(HW_I2C_INT_CLEAR,0x07);
	/* set i2c line idle state */
	MA_OUTW(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	
}
#endif

/*===========================================================================
FUNCTION:  i2c_write

DESCRIPTION: 
  	write data via iic bus
  	
INPUT PARAMETER:
	I2C_RW_CMD_TYPE_T *p_cmd:  iic address and data info
	p					p_cmd->reg_len + p_cmd->len: 1--4; (caller guarentee,don't check here)
OUTPUT PARAMETER:  

RETURN VALUE:
  I2C_SUCCESS          		operation successful 
  I2C_FAILURE          		operation failed 
  I2C_INVALID_LEN      		invalid length specified for I/O 
  I2C_INVALID_ACCESS   	 illegal bus access when busy 

===========================================================================*/
STATUS_T i2c_write(I2C_PARA_T *p_para_i2c,UINT8 *buf)
{
	UINT32 data;
	UINT16 timeout;
	UINT16 i;
	STATUS_T status = RSP_SUCCESS;
	
	if (MA_INW(HW_I2C_TRANSFER_START)!=0x00)/*busy ?*/
	{
		MA_OUTW(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	
	}

	/* set I2C address and ack bit */
	if((p_para_i2c->options & I2C_ACK) ==I2C_ACK)
		MA_OUTW(HW_I2C_SLAVE,(p_para_i2c->slave_addr|I2C_SLAVE_ACK));
	else
		MA_OUTW(HW_I2C_SLAVE,(p_para_i2c->slave_addr|I2C_SLAVE_NACK));

	//data to write
	data = 0;
	for(i=0; i<p_para_i2c->len_per_write; i++)
		data =data | ((UINT32)(*(buf+i))<<((i)*8));

	/* set I2C mode */
	MA_OUTW(HW_I2C_TRANSFER_CONFIG,
			(((UINT32)(p_para_i2c->len_per_write -1 )<<4)|
			I2C_TRANSFER_CONFIG_WRITE|
			I2C_TRANSFER_CONFIG_NORMAL));

	/* set data */
	MA_OUTW(HW_I2C_DATA,data);
	
	/* clear state */
	MA_OUTW(HW_I2C_INT_CLEAR,0x07);	
 
	/* start write */
	MA_OUTW(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_ENABLE);

	do
	{
		/* read status */
		timeout = 0;
		while (((data=MA_INW(HW_I2C_INT_STATUS))&I2C_INT_STATUS_WR_DONE)!=I2C_INT_STATUS_WR_DONE)
		{
			if(timeout++>I2C_TIMEOUT_VALUE)
			{
				status =    RSP_I2C_TIMEOUT;
				break;
			}
		}
		
		if((p_para_i2c->options&I2C_ACK) ==I2C_ACK)
		{
			if ((data&I2C_INT_MASK_ACK_ERR)==I2C_INT_MASK_ACK_ERR)
			{
				status =  RSP_I2C_NO_ACK;
				break;
			}
		}
	}while (0);
	/* clear state */
	MA_OUTW(HW_I2C_INT_CLEAR,0x07);	
	MA_OUTW(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	

	return status;
}


/*===========================================================================
FUNCTION:  i2c_write

DESCRIPTION: 
  	write data via iic bus
  	
INPUT PARAMETER:
	I2C_RW_CMD_TYPE_T *p_cmd:  iic address and data info
	p					p_cmd->reg_len + p_cmd->len: 1--4; (caller guarentee,don't check here)
OUTPUT PARAMETER:  

RETURN VALUE:
  I2C_SUCCESS          		operation successful 
  I2C_FAILURE          		operation failed 
  I2C_INVALID_LEN      		invalid length specified for I/O 
  I2C_INVALID_ACCESS   	 illegal bus access when busy 

===========================================================================*/
STATUS_T i2c_write_h(I2C_PARA_T *p_para_i2c,UINT8 *buf)
{
	UINT32 data;
	UINT16 timeout;
	UINT16 i;
	STATUS_T status = RSP_SUCCESS;
	
	if (inpw(HW_I2C_TRANSFER_START)!=0x00)/*busy ?*/
	{
		outpw(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	
	}

	/* set I2C address and ack bit */
	if((p_para_i2c->options & I2C_ACK) ==I2C_ACK)
		outpw(HW_I2C_SLAVE,(p_para_i2c->slave_addr|I2C_SLAVE_ACK));
	else
		outpw(HW_I2C_SLAVE,(p_para_i2c->slave_addr|I2C_SLAVE_NACK));

	//data to write
	data = 0;
	for(i=0; i<p_para_i2c->len_per_write; i++)
		data =data | ((UINT32)(*(buf+i))<<((i)*8));

	/* set I2C mode */
	outpw(HW_I2C_TRANSFER_CONFIG,
			(((UINT32)(p_para_i2c->len_per_write -1 )<<4)|
			I2C_TRANSFER_CONFIG_WRITE|
			I2C_TRANSFER_CONFIG_NORMAL));

	/* set data */
	outpw(HW_I2C_DATA,data);
	
	/* clear state */
	outpw(HW_I2C_INT_CLEAR,0x07);	
 
	/* start write */
	outpw(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_ENABLE);

	do
	{
		/* read status */
		timeout = 0;
		while (((data=inpw(HW_I2C_INT_STATUS))&I2C_INT_STATUS_WR_DONE)!=I2C_INT_STATUS_WR_DONE)
		{
			if(timeout++>I2C_TIMEOUT_VALUE)
			{
				status =    RSP_I2C_TIMEOUT;
				break;
			}
		}
		
		if((p_para_i2c->options&I2C_ACK) ==I2C_ACK)
		{
			if ((data&I2C_INT_MASK_ACK_ERR)==I2C_INT_MASK_ACK_ERR)
			{
				status =  RSP_I2C_NO_ACK;
				break;
			}
		}
	}while (0);
	/* clear state */
	outpw(HW_I2C_INT_CLEAR,0x07);	
	outpw(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	

	return status;
}
/*===========================================================================
FUNCTION:  i2c_read_normal

DESCRIPTION: 
  
INPUT PARAMETER:

OUTPUT PARAMETER:  

RETURN VALUE:
  I2C_SUCCESS		   operation successful 
  I2C_FAILURE		   operation failed 
  I2C_INVALID_LEN	   invalid length specified for I/O 
  I2C_INVALID_ACCESS	illegal bus access when busy 

===========================================================================*/
STATUS_T i2c_read(I2C_PARA_T *p_para_i2c,UINT8 *buf,UINT8* read_buf)
{
	STATUS_T status = RSP_SUCCESS;
	UINT16 i;
	UINT32 temp,timeout;
	
	if (MA_INW(HW_I2C_TRANSFER_START)!=0x00)/*busy ?*/
	{
		MA_OUTW(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	
	}

	//first state, write reg addr(and store data len)
	//status = i2c_write(p_para_i2c,buf);
	status = i2c_write(p_para_i2c,buf);
	if (status != RSP_SUCCESS)
		return status;

	/* set I2C mode */
	MA_OUTW(HW_I2C_TRANSFER_CONFIG,
				(((p_para_i2c->len_per_read- 1)<<4)|
				I2C_TRANSFER_CONFIG_READ|
				I2C_TRANSFER_CONFIG_NORMAL));

	/* clear state */
	MA_OUTW(HW_I2C_INT_CLEAR,0x07);	
	/* start read */
	MA_OUTW(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_ENABLE);

	/* read status */
	timeout = 0;
	do
	{
		while (MA_INWM(HW_I2C_INT_STATUS,I2C_INT_STATUS_RD_DONE)!=I2C_INT_STATUS_RD_DONE)
		{
			if(timeout++>I2C_TIMEOUT_VALUE)
			{
				status =    RSP_I2C_TIMEOUT;
				break;
			}
		}
		
		if((p_para_i2c->options&I2C_ACK) ==I2C_ACK)
		{
			if (MA_INWM(HW_I2C_INT_STATUS,I2C_INT_MASK_ACK_ERR)==I2C_INT_MASK_ACK_ERR)
			{
				status =  RSP_I2C_NO_ACK;
				break;
			}
		}
	}while (0);
	if (status == RSP_SUCCESS)
		temp = MA_INW(HW_I2C_DATA);
	else
		temp = 0;
	for(i=0;i<p_para_i2c->len_per_read;i++)
		*(read_buf+i) = (UINT8)(temp>>(i*8));

	/* clear state */
	MA_OUTW(HW_I2C_INT_CLEAR,0x07);	
	MA_OUTW(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	

	return status; 
}

STATUS_T i2c_read_h(I2C_PARA_T *p_para_i2c,UINT8 *buf,UINT8* read_buf)
{
	STATUS_T status = RSP_SUCCESS;
	UINT16 i;
	UINT32 temp,timeout;
	
	if (inpw(HW_I2C_TRANSFER_START)!=0x00)/*busy ?*/
	{
		outpw(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	
	}

	//first state, write reg addr(and store data len)
	//status = i2c_write(p_para_i2c,buf);
	status = i2c_write_h(p_para_i2c,buf);
	if (status != RSP_SUCCESS)
		return status;

	/* set I2C mode */
	outpw(HW_I2C_TRANSFER_CONFIG,
				(((p_para_i2c->len_per_read- 1)<<4)|
				I2C_TRANSFER_CONFIG_READ|
				I2C_TRANSFER_CONFIG_NORMAL));

	/* clear state */
	outpw(HW_I2C_INT_CLEAR,0x07);	
	/* start read */
	outpw(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_ENABLE);

	/* read status */
	timeout = 0;
	do
	{
		while ((inpw(HW_I2C_INT_STATUS)&I2C_INT_STATUS_RD_DONE)!=I2C_INT_STATUS_RD_DONE)
		{
			if(timeout++>I2C_TIMEOUT_VALUE)
			{
				status =    RSP_I2C_TIMEOUT;
				break;
			}
		}
		
		if((p_para_i2c->options&I2C_ACK) ==I2C_ACK)
		{
			if ((inpw(HW_I2C_INT_STATUS)&I2C_INT_MASK_ACK_ERR)==I2C_INT_MASK_ACK_ERR)
			{
				status =  RSP_I2C_NO_ACK;
				break;
			}
		}
	}while (0);
	if (status == RSP_SUCCESS)
		temp = inpw(HW_I2C_DATA);
	else
		temp = 0;
	for(i=0;i<p_para_i2c->len_per_read;i++)
		*(read_buf+i) = (UINT8)(temp>>(i*8));

	/* clear state */
	outpw(HW_I2C_INT_CLEAR,0x07);	
	outpw(HW_I2C_TRANSFER_START,I2C_TRANSFER_START_DISABLE);	

	return status; 
}
STATUS_T i2c_write_buffer(I2C_REQ_T * i2c_req)
{
	UINT8 *pbuf;
	STATUS_T status = RSP_SUCCESS;
	int i,retrytime;
	
	for (i=0;i<i2c_req->data_size;i+=i2c_req->p_para_i2c->len_per_write)
	{	
		pbuf = i2c_req->data + i;
		for (retrytime=0;retrytime<=i2c_req->p_para_i2c->retry_time;retrytime++)
		{
			status = i2c_write_h(i2c_req->p_para_i2c,pbuf);
			if (status == RSP_SUCCESS)
				break;
		}
		if (retrytime >i2c_req->p_para_i2c->retry_time)
			break;
		//delay between two transfer
		delay(i2c_req->p_para_i2c->access_interval);
	}
	return status;
}

STATUS_T i2c_read_buffer(I2C_REQ_T *i2c_req)
{
	UINT8 *pbuf;
	UINT8 *pbuf_read;
	STATUS_T status = RSP_SUCCESS;
	int i,j,retrytime;

	for (i=0,j=0;i<i2c_req->data_size;i+=i2c_req->p_para_i2c->len_per_write,j+=i2c_req->p_para_i2c->len_per_read)
	{	
		pbuf = i2c_req->data + i*2;
		pbuf_read = i2c_req->result + j*2;
		for (retrytime=0;retrytime<=i2c_req->p_para_i2c->retry_time;retrytime++)
		{
			status = i2c_read(i2c_req->p_para_i2c,pbuf,pbuf_read);
			if (status == RSP_SUCCESS)
				break;
		}
		if (retrytime >i2c_req->p_para_i2c->retry_time)
			break;
		//delay between two transfer
		delay(i2c_req->p_para_i2c->access_interval/100);
	}
	return status;
}

